import type { AutomationOptions, IBrowserManager } from '@shared/types/'
import type { Browser, BrowserContext, Page } from 'playwright'
import { existsSync } from 'node:fs'
import { writeFile } from 'node:fs/promises'
import { join } from 'node:path'
import { generateRandomFingerprint } from '@shared/utils/fingerprint'
import { getConfig, getTempHtmlPath } from '../config/index'
import {
  createContextLogger,
  generateTraceId,
  log,
  PerformanceLogger,
} from '../logger/index'

export class BrowserManager implements IBrowserManager {
  private browser: Browser | null = null
  private context: BrowserContext | null = null
  private page: Page | null = null
  private logger = createContextLogger({ component: 'BrowserManager' })
  private operationId: string = ''

  async launch(options?: AutomationOptions): Promise<void> {
    this.operationId = generateTraceId()

    try {
      // 触发配置初始化以确保日志/路径依赖可用（返回值无需直接使用）
      void getConfig()
      PerformanceLogger.start(`browser-launch-${this.operationId}`)

      this.logger.info('🚀 开始启动浏览器', {
        operationId: this.operationId,
        headless: options?.headless ?? true,
        timeout: options?.timeout ?? 30000,
      })

      // 生产环境下，明确指定 Playwright 浏览器安装路径，避免回落到用户目录 ms-playwright
      let executablePath: string | undefined
      if (process.env.NODE_ENV === 'production') {
        if (!process.env.PLAYWRIGHT_BROWSERS_PATH) {
          const maybe = join(process.resourcesPath || process.cwd(), 'browsers')
          process.env.PLAYWRIGHT_BROWSERS_PATH = maybe
        }
        if (!process.env.PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD)
          process.env.PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD = '1'
        const base = join(process.resourcesPath || process.cwd(), 'browsers')
        const candidates = [
          join(base, 'chromium-1193', 'chrome-win', 'chrome.exe'),
          join(base, 'chromium_headless_shell-1193', 'chrome-win', 'headless_shell.exe'),
        ]
        const found = candidates.find(p => existsSync(p))
        if (found)
          executablePath = found
      }

      // 动态导入 playwright，确保环境变量已生效
      const { chromium } = await import('playwright')

      // 启动浏览器
      this.browser = await chromium.launch({
        headless: options?.headless ?? true,
        timeout: options?.timeout ?? 30000,
        args: [
          '--no-sandbox',
          '--disable-setuid-sandbox',
          '--disable-dev-shm-usage',
          '--disable-accelerated-2d-canvas',
          '--no-first-run',
          '--no-zygote',
          '--disable-gpu',
        ],
        executablePath,
      })

      // 创建浏览器上下文
      const fp = generateRandomFingerprint()
      this.context = await this.browser.newContext({
        userAgent: options?.user_agent ? options.user_agent : fp.userAgent,
        viewport: options?.viewport || { width: 1366, height: 768 },
        locale: 'zh-CN',
        timezoneId: 'Asia/Hong_Kong',
        // 设置额外的 HTTP 头
        extraHTTPHeaders: {
          'Accept-Language': fp.acceptLanguage,
          ...(fp.secChUa ? { 'sec-ch-ua': fp.secChUa } : {}),
          ...(fp.secChUaMobile ? { 'sec-ch-ua-mobile': fp.secChUaMobile } : {}),
          ...(fp.secChUaPlatform ? { 'sec-ch-ua-platform': fp.secChUaPlatform } : {}),
        },
      })

      // 创建页面
      this.page = await this.context.newPage()

      // 设置页面超时
      this.page.setDefaultTimeout(options?.timeout ?? 30000)

      // 监听页面事件
      this.page.on('console', (msg) => {
        if (msg.type() === 'error') {
          this.logger.warn('页面控制台错误', {
            operationId: this.operationId,
            message: msg.text(),
          })
        }
      })

      this.page.on('pageerror', (error) => {
        try {
          const msg = (error && (error as any).message) ? (error as any).message : String(error)
          const ignoreList = [
            /Cannot read properties of null \(reading 'style'\)/i,
            /ResizeObserver loop limit exceeded/i,
            /Script error\.?/i,
          ]
          if (ignoreList.some(r => r.test(msg))) {
            this.logger.debug('页面错误(已忽略)', {
              operationId: this.operationId,
              message: msg,
              url: (() => {
                try { return this.page?.url() }
                catch { return undefined }
              })(),
            })
            return
          }
          this.logger.warn('页面错误', {
            operationId: this.operationId,
            error: msg,
            url: (() => {
              try { return this.page?.url() }
              catch { return undefined }
            })(),
          })
        }
        catch {
          this.logger.warn('页面错误', {
            operationId: this.operationId,
            error: String(error),
          })
        }
      })

      const launchDuration = PerformanceLogger.end(`browser-launch-${this.operationId}`)
      log.performance('browser-launch', launchDuration, {
        operationId: this.operationId,
        component: 'BrowserManager',
      })

      this.logger.info('🌐 浏览器启动成功', {
        operationId: this.operationId,
        duration: launchDuration,
        durationText: `${launchDuration}ms`,
      })
    }
    catch (error) {
      const launchDuration = PerformanceLogger.measure(`browser-launch-${this.operationId}`) || 0

      if (error instanceof Error) {
        log.system.error(error, 'BrowserManager.launch', {
          operationId: this.operationId,
          user_action: '系统尝试启动浏览器',
          recovery_suggestion: '检查系统资源和浏览器安装，稍后重试',
        })
      }
      else {
        this.logger.error('浏览器启动失败', {
          operationId: this.operationId,
          error: String(error),
          duration: launchDuration,
          durationText: `${launchDuration}ms`,
        })
      }
      throw error
    }
  }

  async close(): Promise<void> {
    const closeOperationId = generateTraceId()
    PerformanceLogger.start(`browser-close-${closeOperationId}`)

    try {
      this.logger.info('🔄 开始关闭浏览器', { operationId: closeOperationId })

      if (this.page) {
        await this.page.close()
        this.page = null
        this.logger.debug('页面已关闭', { operationId: closeOperationId })
      }

      if (this.context) {
        await this.context.close()
        this.context = null
        this.logger.debug('浏览器上下文已关闭', { operationId: closeOperationId })
      }

      if (this.browser) {
        await this.browser.close()
        this.browser = null
        this.logger.debug('浏览器实例已关闭', { operationId: closeOperationId })
      }

      const closeDuration = PerformanceLogger.end(`browser-close-${closeOperationId}`)
      log.performance('browser-close', closeDuration, {
        operationId: closeOperationId,
        component: 'BrowserManager',
      })

      this.logger.info('🔒 浏览器已关闭', {
        operationId: closeOperationId,
        duration: closeDuration,
        durationText: `${closeDuration}ms`,
      })
    }
    catch (error) {
      const closeDuration = PerformanceLogger.measure(`browser-close-${closeOperationId}`) || 0

      if (error instanceof Error) {
        log.system.error(error, 'BrowserManager.close', {
          operationId: closeOperationId,
          user_action: '系统尝试关闭浏览器',
          recovery_suggestion: '忽略此错误，浏览器资源可能已被释放',
        })
      }
      else {
        this.logger.error('关闭浏览器失败', {
          operationId: closeOperationId,
          error: String(error),
          duration: closeDuration,
          durationText: `${closeDuration}ms`,
        })
      }
      throw error
    }
  }

  async newPage(): Promise<Page> {
    if (!this.context) {
      throw new Error('浏览器上下文未初始化')
    }

    const page = await this.context.newPage()
    return page
  }

  getCurrentPage(): Page | null {
    return this.page
  }

  async saveHtml(filename: string, html?: string): Promise<string> {
    const saveOperationId = generateTraceId()
    PerformanceLogger.start(`html-save-${saveOperationId}`)

    try {
      const tempPath = getTempHtmlPath()
      const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
      const fullFilename = `${timestamp}_${filename}.html`
      const filePath = join(tempPath, fullFilename)

      this.logger.debug('开始保存HTML文件', {
        operationId: saveOperationId,
        filename: fullFilename,
        hasProvidedContent: !!html,
      })

      let content = html
      if (!content && this.page) {
        content = await this.page.content()
      }

      if (content) {
        await writeFile(filePath, content, 'utf-8')
        const saveDuration = PerformanceLogger.end(`html-save-${saveOperationId}`)

        this.logger.debug('HTML文件保存成功', {
          operationId: saveOperationId,
          filePath,
          contentSize: `${(content.length / 1024).toFixed(2)}KB`,
          duration: saveDuration,
          durationText: `${saveDuration}ms`,
        })

        return filePath
      }
      else {
        throw new Error('无内容可保存')
      }
    }
    catch (error) {
      const saveDuration = PerformanceLogger.measure(`html-save-${saveOperationId}`) || 0

      if (error instanceof Error) {
        log.system.error(error, 'BrowserManager.saveHtml', {
          operationId: saveOperationId,
          filename,
          user_action: '系统尝试保存HTML快照',
          recovery_suggestion: '检查磁盘空间和文件权限',
        })
      }
      else {
        this.logger.error('保存HTML失败', {
          operationId: saveOperationId,
          filename,
          error: String(error),
          duration: saveDuration,
          durationText: `${saveDuration}ms`,
        })
      }
      throw error
    }
  }

  async screenshot(filename?: string): Promise<string> {
    if (!this.page) {
      throw new Error('页面未初始化')
    }

    const screenshotOperationId = generateTraceId()
    PerformanceLogger.start(`screenshot-${screenshotOperationId}`)

    try {
      const tempPath = getTempHtmlPath()
      const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
      const fullFilename = filename || `screenshot_${timestamp}.png`
      const filePath = join(tempPath, fullFilename)

      this.logger.debug('开始截图', {
        operationId: screenshotOperationId,
        filename: fullFilename,
        fullPage: true,
      })

      await this.page.screenshot({
        path: filePath,
        fullPage: true,
      })

      const screenshotDuration = PerformanceLogger.end(`screenshot-${screenshotOperationId}`)

      this.logger.debug('截图已保存', {
        operationId: screenshotOperationId,
        filePath,
        duration: screenshotDuration,
        durationText: `${screenshotDuration}ms`,
      })

      return filePath
    }
    catch (error) {
      const screenshotDuration = PerformanceLogger.measure(`screenshot-${screenshotOperationId}`) || 0

      if (error instanceof Error) {
        log.system.error(error, 'BrowserManager.screenshot', {
          operationId: screenshotOperationId,
          filename,
          user_action: '系统尝试截图',
          recovery_suggestion: '检查页面状态和磁盘空间',
        })
      }
      else {
        this.logger.error('截图失败', {
          operationId: screenshotOperationId,
          filename,
          error: String(error),
          duration: screenshotDuration,
          durationText: `${screenshotDuration}ms`,
        })
      }
      throw error
    }
  }
}

// 创建全局浏览器管理器实例
let browserManager: BrowserManager | null = null

export function getBrowserManager(): BrowserManager {
  if (!browserManager) {
    browserManager = new BrowserManager()
  }
  return browserManager
}

// 清理资源
export async function cleanupBrowser(): Promise<void> {
  const cleanupLogger = createContextLogger({ component: 'BrowserManager.cleanup' })
  const cleanupOperationId = generateTraceId()

  if (browserManager) {
    cleanupLogger.info('开始清理浏览器资源', { operationId: cleanupOperationId })

    try {
      await browserManager.close()
      browserManager = null

      cleanupLogger.info('浏览器资源清理完成', { operationId: cleanupOperationId })
    }
    catch (error) {
      cleanupLogger.warn('清理浏览器资源时出现问题', {
        operationId: cleanupOperationId,
        error: String(error),
      })
      // 强制设置为null，即使关闭失败
      browserManager = null
    }
  }
  else {
    cleanupLogger.debug('无需清理，浏览器资源已为空', { operationId: cleanupOperationId })
  }
}
